using System;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;

namespace Freshbooks.Client
{
    public class APIClient
    {
        private Uri accessUrl;
        private XmlNodeList actualResponse;
        private string authenticationToken;

        public APIClient()
        { }

        public APIClient(string AccessLink, string Token)
            : this(new Uri(AccessLink), Token)
        { }

        public APIClient(Uri AccessLink, string Token)
        {
            accessUrl = AccessLink;
            authenticationToken = Token;

            Client = new ClientClass(this);
            Invoice = new InvoiceClass(this);
        }

        public ClientClass Client { get; set; }
        public InvoiceClass Invoice { get; set; }

        public Uri AccessUrl
        {
            get { return accessUrl; }
            set { accessUrl = value; }
        }

        public XmlNodeList ActualResponse
        {
            get { return actualResponse; }
            set { actualResponse = value; }
        }

        public string AuthenticationToken
        {
            get { return authenticationToken; }
            set { authenticationToken = value; }
        }

        public class ClientClass
        {
            private readonly APIClient _apiClient;

            public ClientClass(APIClient apiClient)
            {
                _apiClient = apiClient;
            }

            public void CreateClient(Client currentClient)
            {
                _apiClient.Call(ClientCommand.GetCreateXml(currentClient));
            }
        }

        public class InvoiceClass
        {
            private readonly APIClient _apiClient;

            public InvoiceClass(APIClient apiClient)
            {
                _apiClient = apiClient;
            }

            public void CreateInvoice(Invoice currentInvoice)
            {
                _apiClient.Call(InvoiceCommand.GetCreateXml(currentInvoice));
            }

            public void DeleteInvoice(Invoice currentInvoice)
            {
                _apiClient.Call(InvoiceCommand.GetInvoiceDeleteXml(currentInvoice));
            }

            public void SendByEmailInvoice(Invoice currentInvoice)
            {
                _apiClient.Call(InvoiceCommand.GetInvoiceByEmailXml(currentInvoice));
            }

            public void SendByEmailInvoiceCustomMessage(Invoice currentInvoice, string subject, string message)
            {
                _apiClient.Call(InvoiceCommand.GetInvoiceByEmailXml(currentInvoice, subject, message));
            }

            public void UpdateInvoice(Invoice currentInvoice)
            {
                _apiClient.Call(InvoiceCommand.GetInvoiceUpdateXml(currentInvoice));
            }
        }

        public static Invoice RetrieveInvoice(int invoice)
        {
            var invoice2 = new Invoice
                               {
                                   InvoiceFreshBooksId = invoice
                               };
            return invoice2;
        }

        public XmlNodeList ProcessResponse(HttpWebResponse response)
        {
            var document = new XmlDocument();
            document.Load(response.GetResponseStream());
            XmlNodeList elementsByTagName = document.GetElementsByTagName("response");
            if (!elementsByTagName[0].Attributes["status"].Value.Equals("ok"))
            {
                throw new ApplicationException(elementsByTagName[0].FirstChild.InnerText);
            }
            return elementsByTagName;
        }

        private void Call(string xml)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(xml);
            WebRequest request = WebRequest.Create(accessUrl);
            request.Credentials = new NetworkCredential(authenticationToken, "X");
            request.Method = "POST";
            Stream requestStream = request.GetRequestStream();
            requestStream.Write(bytes, 0, bytes.Length);
            requestStream.Close();
            try
            {
                var response = (HttpWebResponse)request.GetResponse();
                actualResponse = ProcessResponse(response);
            }
            catch (Exception exception)
            {
                throw new ApplicationException(exception.Message);
            }
        }
    }
}